{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Learning from Clicks "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "ename": "IndentationError",
     "evalue": "unindent does not match any outer indentation level (<ipython-input-4-dee5efce02e2>, line 74)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-4-dee5efce02e2>\"\u001b[0;36m, line \u001b[0;32m74\u001b[0m\n\u001b[0;31m    def setupnetwork(self,wordids,urlids):\u001b[0m\n\u001b[0m                                          ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m unindent does not match any outer indentation level\n"
     ]
    }
   ],
   "source": [
    "from math import tanh\n",
    "import sqlite3\n",
    "\n",
    "class searchnet:\n",
    "    def __init__(self,dbname):\n",
    "        self.con=sqlite3.connect(dbname)\n",
    "  \n",
    "    def __del__(self):\n",
    "        self.con.close()\n",
    "\n",
    "    def maketables(self):\n",
    "        self.con.execute('create table hiddennode(create_key)')\n",
    "        self.con.execute('create table wordhidden(fromid,toid,strength)')\n",
    "        self.con.execute('create table hiddenurl(fromid,toid,strength)')\n",
    "        self.con.commit()\n",
    "        \n",
    "    def getstrength(self,fromid,toid,layer):\n",
    "        if layer==0: \n",
    "            table='wordhidden'\n",
    "        else: \n",
    "            table='hiddenurl'\n",
    "        res=self.con.execute('select strength from %s where fromid=%d and toid=%d' % (table,fromid,toid)).fetchone()\n",
    "        if res==None: \n",
    "            if layer==0: \n",
    "                return -0.2\n",
    "            if layer==1: \n",
    "                return 0\n",
    "        return res[0]\n",
    "    \n",
    "    def setstrength(self,fromid,toid,layer,strength):\n",
    "        if layer==0: \n",
    "            table='wordhidden'\n",
    "        else: \n",
    "            table='hiddenurl'\n",
    "        res=self.con.execute('select rowid from %s where fromid=%d and toid=%d' % (table,fromid,toid)).fetchone()\n",
    "        if res==None: \n",
    "            self.con.execute('insert into %s (fromid,toid,strength) values (%d,%d,%f)' % (table,fromid,toid,strength))\n",
    "        else:\n",
    "            rowid=res[0]\n",
    "            self.con.execute('update %s set strength=%f where rowid=%d' % (table,strength,rowid))\n",
    "            \n",
    "    def generatehiddennode(self,wordids,urls):\n",
    "        if len(wordids)>3: \n",
    "            return None\n",
    "        # Check if we already created a node for this set of words\n",
    "        sorted_words=[str(id) for id in wordids]\n",
    "        sorted_words.sort()\n",
    "        createkey='_'.join(sorted_words)\n",
    "        res=self.con.execute(\"select rowid from hiddennode where create_key='%s'\" % createkey).fetchone()\n",
    "\n",
    "        # If not, create it\n",
    "        if res==None:\n",
    "            cur=self.con.execute(\"insert into hiddennode (create_key) values ('%s')\" % createkey)\n",
    "            hiddenid=cur.lastrowid\n",
    "            # Put in some default weights\n",
    "            for wordid in wordids:\n",
    "                self.setstrength(wordid,hiddenid,0,1.0/len(wordids))\n",
    "            for urlid in urls:\n",
    "                self.setstrength(hiddenid,urlid,1,0.1)\n",
    "            self.con.commit()\n",
    "            \n",
    "def getallhiddenids(self,wordids,urlids):\n",
    "      l1={}\n",
    "      for wordid in wordids:\n",
    "        cur=self.con.execute(\n",
    "        'select toid from wordhidden where fromid=%d' % wordid)\n",
    "        for row in cur: l1[row[0]]=1\n",
    "      for urlid in urlids:\n",
    "        cur=self.con.execute(\n",
    "        'select fromid from hiddenurl where toid=%d' % urlid)\n",
    "        for row in cur: l1[row[0]]=1\n",
    "      return l1.keys()\n",
    "\n",
    "    def setupnetwork(self,wordids,urlids):\n",
    "        # value lists\n",
    "        self.wordids=wordids\n",
    "        self.hiddenids=self.getallhiddenids(wordids,urlids)\n",
    "        self.urlids=urlids\n",
    " \n",
    "        # node outputs\n",
    "        self.ai = [1.0]*len(self.wordids)\n",
    "        self.ah = [1.0]*len(self.hiddenids)\n",
    "        self.ao = [1.0]*len(self.urlids)\n",
    "        \n",
    "        # create weights matrix\n",
    "        self.wi = [[self.getstrength(wordid,hiddenid,0) \n",
    "                    for hiddenid in self.hiddenids] \n",
    "                   for wordid in self.wordids]\n",
    "        self.wo = [[self.getstrength(hiddenid,urlid,1) \n",
    "                    for urlid in self.urlids] \n",
    "                   for hiddenid in self.hiddenids]\n",
    "\n",
    "    def feedforward(self):\n",
    "        # the only inputs are the query words\n",
    "        for i in range(len(self.wordids)):\n",
    "            self.ai[i] = 1.0\n",
    "\n",
    "        # hidden activations\n",
    "        for j in range(len(self.hiddenids)):\n",
    "            sum = 0.0\n",
    "            for i in range(len(self.wordids)):\n",
    "                sum = sum + self.ai[i] * self.wi[i][j]\n",
    "            self.ah[j] = tanh(sum)\n",
    "\n",
    "        # output activations\n",
    "        for k in range(len(self.urlids)):\n",
    "            sum = 0.0\n",
    "            for j in range(len(self.hiddenids)):\n",
    "                sum = sum + self.ah[j] * self.wo[j][k]\n",
    "            self.ao[k] = tanh(sum)\n",
    "\n",
    "        return self.ao[:]\n",
    "\n",
    "    def getresult(self,wordids,urlids):\n",
    "        self.setupnetwork(wordids,urlids)\n",
    "        return self.feedforward()\n",
    "\n",
    "    def backPropagate(self, targets, N=0.5):\n",
    "        # calculate errors for output\n",
    "        output_deltas = [0.0] * len(self.urlids)\n",
    "        for k in range(len(self.urlids)):\n",
    "            error = targets[k]-self.ao[k]\n",
    "            output_deltas[k] = dtanh(self.ao[k]) * error\n",
    "\n",
    "        # calculate errors for hidden layer\n",
    "        hidden_deltas = [0.0] * len(self.hiddenids)\n",
    "        for j in range(len(self.hiddenids)):\n",
    "            error = 0.0\n",
    "            for k in range(len(self.urlids)):\n",
    "                error = error + output_deltas[k]*self.wo[j][k]\n",
    "            hidden_deltas[j] = dtanh(self.ah[j]) * error\n",
    "\n",
    "        # update output weights\n",
    "        for j in range(len(self.hiddenids)):\n",
    "            for k in range(len(self.urlids)):\n",
    "                change = output_deltas[k]*self.ah[j]\n",
    "                self.wo[j][k] = self.wo[j][k] + N*change\n",
    "\n",
    "        # update input weights\n",
    "        for i in range(len(self.wordids)):\n",
    "            for j in range(len(self.hiddenids)):\n",
    "                change = hidden_deltas[j]*self.ai[i]\n",
    "                self.wi[i][j] = self.wi[i][j] + N*change\n",
    "\n",
    "    def trainquery(self,wordids,urlids,selectedurl): \n",
    "        # generate a hidden node if necessary\n",
    "        self.generatehiddennode(wordids,urlids)\n",
    "\n",
    "        self.setupnetwork(wordids,urlids)      \n",
    "        self.feedforward()\n",
    "        targets=[0.0]*len(urlids)\n",
    "        targets[urlids.index(selectedurl)]=1.0\n",
    "        error = self.backPropagate(targets)\n",
    "        self.updatedatabase()\n",
    "\n",
    "    def updatedatabase(self):\n",
    "        # set them to database values\n",
    "        for i in range(len(self.wordids)):\n",
    "            for j in range(len(self.hiddenids)):\n",
    "                self.setstrength(self.wordids[i],self. hiddenids[j],0,self.wi[i][j])\n",
    "        for j in range(len(self.hiddenids)):\n",
    "            for k in range(len(self.urlids)):\n",
    "                self.setstrength(self.hiddenids[j],self.urlids[k],1,self.wo[j][k])\n",
    "        self.con.commit()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "mynet = searchnet('nn.db')\n",
    "mynet.maketables()\n",
    "wWorld"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python [conda root]",
   "language": "python",
   "name": "conda-root-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
